#ifndef __CMATRIX4D_H__
#define __CMATRIX4D_H__

#include "StdDefs.h"
#include "CMathFixed.h"
#include "CVector3d.h"
#include "CVector4d.h"
#include "CMatrix4dh.h"



// CMatrix4d* must maintain casting ability with (fixed*), as well as, CMatrix4dh.
// CMatrix4d may not have virtuals, inherited, or instantiated data.
#define CMatrix4d_ToCMatrix4dh(m)						( *(CMatrix4dh*)&(m) )
#define CMatrix4d_ptrToCMatrix4dhPtr(m)					( (CMatrix4dh*)(m) )
#define CMatrix4d_FromCMatrix4dh(m)						( *(CMatrix4d*)&(m) )
#define CMatrix4d_ptrFromCMatrix4dhPtr(m)				( (CMatrix4d*)(m) )

 

class CMatrix4d 
{
public:
	friend class CMatrix3d;

public:
	CMatrix4d();
	CMatrix4d( const fixed* m );
	CMatrix4d( fixed m00, fixed m01, fixed m02, fixed m03, 
			   fixed m10, fixed m11, fixed m12, fixed m13,
			   fixed m20, fixed m21, fixed m22, fixed m23,
			   fixed m30, fixed m31, fixed m32, fixed m33,
			   int16 nOfArthmeticOpsTriggeringOrientationFixup = 0 ); 
			 // parameter columns are rows in the matrix!
	CMatrix4d( const CVector4d* col, 
			   int16 nOfArthmeticOpsTriggeringOrientationFixup = 0 );
	CMatrix4d( const CVector4d& col0, const CVector4d& col1, 
			   const CVector4d& col2, const CVector4d& col3,
			   int16 nOfArthmeticOpsTriggeringOrientationFixup = 0 ); 
	CMatrix4d( const CMatrix4d& m );
	~CMatrix4d(){}

	CMatrix4d&			Clear();
	CMatrix4d&			LoadIdentity();
	CMatrix4d&			Orthonormalize();
	CMatrix4d&			Scale( fixed si, fixed sj, fixed sk );
	CMatrix4d&			Scale( const CVector3d& s );
	CMatrix4d&			RotateX( fixed degrees );
	CMatrix4d&			RotateY( fixed degrees );
	CMatrix4d&			RotateZ( fixed degrees );
	CMatrix4d&			Rotate( fixed degrees, fixed i, fixed j, fixed k );
	CMatrix4d&			Rotate( fixed degrees, const CVector3d& axis );
	CMatrix4d&			Translate( fixed i, fixed j, fixed k );
	CMatrix4d&			Translate( const CVector3d& v );

	int16				GetNumOfArithmeticOpsSinceLastFixup() const;
	void				SetNumOfArithmeticOpsTriggeringOrientationFixup( int16 val );
	int16				GetNumOfArithmeticOpsTriggeringOrientationFixup() const;

	CMatrix4d&			Transpose();

	__inline operator	fixed const*() const { return (fixed const*)m_col0; }
	__inline operator	fixed*() { return (fixed*)m_col0; }

	CMatrix4d&			operator=( const fixed* col ); 
	CMatrix4d&			operator=( const CVector4d* col );

	CMatrix4d&			operator*=( const CMatrix4d& b );

	fixed const&		operator[]( int idx ) const;
	fixed&				operator[]( int idx );

	friend CMatrix4d	operator*( const CMatrix4d& a, const CMatrix4d& b );

	CVector4d			m_col0;
	CVector4d			m_col1;
	CVector4d			m_col2;
	CVector4d			m_col3;

protected:
	void				IncrementOperationCount();
	void				ConsiderOrientationFixup();

	boolean				m_orientationFixupIsApplicable;
	uint8				m_reserved[3];
	uint16				m_nOfArithmeticOpsSinceLastFixup;
	uint16				m_nOfArthmeticOpsTriggeringOrientationFixup;
};





inline
CMatrix4d::CMatrix4d( const CMatrix4d& m )
{
	*this = m;
}


inline
CMatrix4d& CMatrix4d::Clear()
{
	MEMSET( this, 0, sizeof( CVector4d ) * 4 );

	m_nOfArithmeticOpsSinceLastFixup = 0;

	return *this;
}

inline
CMatrix4d& CMatrix4d::LoadIdentity()
{
	return CMatrix4d_FromCMatrix4dh( CMatrix4d_ToCMatrix4dh( *this ).LoadIdentity() );
}


inline
CMatrix4d& CMatrix4d::Orthonormalize()
{
	return CMatrix4d_FromCMatrix4dh( CMatrix4d_ToCMatrix4dh( *this ).Orthonormalize() );
}


inline
CMatrix4d& CMatrix4d::Scale( fixed si, fixed sj, fixed sk )
{
	
	return CMatrix4d_FromCMatrix4dh( CMatrix4d_ToCMatrix4dh( *this ).Scale( si, sj, sk ) );
}


inline
CMatrix4d& CMatrix4d::Scale( const CVector3d& s )
{
	return CMatrix4d_FromCMatrix4dh( CMatrix4d_ToCMatrix4dh( *this ).Scale( s ) );
}


inline
CMatrix4d& CMatrix4d::RotateX( fixed degrees )
{
	return CMatrix4d_FromCMatrix4dh( CMatrix4d_ToCMatrix4dh( *this ).RotateX( degrees ) );
}


inline
CMatrix4d&	CMatrix4d::RotateY( fixed degrees )
{
	return CMatrix4d_FromCMatrix4dh( CMatrix4d_ToCMatrix4dh( *this ).RotateY( degrees ) );
}


inline
CMatrix4d&	CMatrix4d::RotateZ( fixed degrees )
{
	return CMatrix4d_FromCMatrix4dh( CMatrix4d_ToCMatrix4dh( *this ).RotateZ( degrees ) );
}


inline
CMatrix4d&	CMatrix4d::Rotate( fixed degrees, fixed i, fixed j, fixed k )
{
	return CMatrix4d_FromCMatrix4dh( CMatrix4d_ToCMatrix4dh( *this ).Rotate( degrees, i, j, k ) );
}


inline
CMatrix4d& CMatrix4d::Rotate( fixed degrees, const CVector3d& axis )
{
	return CMatrix4d_FromCMatrix4dh( CMatrix4d_ToCMatrix4dh( *this ).Rotate( degrees, axis ) );
}


inline
CMatrix4d& CMatrix4d::Translate( fixed i, fixed j, fixed k ) 
{ 
	return CMatrix4d_FromCMatrix4dh( CMatrix4d_ToCMatrix4dh( *this ).Translate( i, j, k ) );
}


inline
CMatrix4d&	CMatrix4d::Translate( const CVector3d& v )
{
	return CMatrix4d_FromCMatrix4dh( CMatrix4d_ToCMatrix4dh( *this ).Translate( v ) );
}


inline
int16 CMatrix4d::GetNumOfArithmeticOpsSinceLastFixup() const
{
	return m_nOfArithmeticOpsSinceLastFixup;
}


inline
void CMatrix4d::SetNumOfArithmeticOpsTriggeringOrientationFixup( int16 val )
{
	m_nOfArthmeticOpsTriggeringOrientationFixup = val;

	if( m_nOfArthmeticOpsTriggeringOrientationFixup )
		m_orientationFixupIsApplicable = TRUE;
	else
		m_orientationFixupIsApplicable = FALSE;
}


inline
int16 CMatrix4d::GetNumOfArithmeticOpsTriggeringOrientationFixup() const
{
	return m_nOfArthmeticOpsTriggeringOrientationFixup;
}


inline
CMatrix4d& CMatrix4d::operator=( const fixed* m )
{ 
	ASSERT( m ); 

	MEMCPY( this, m, sizeof( CMatrix4d ) );

	ASSERT( !m_orientationFixupIsApplicable || 
			( m_orientationFixupIsApplicable && m_nOfArthmeticOpsTriggeringOrientationFixup ) );

	return *this;
}


inline
CMatrix4d& CMatrix4d::operator=( const CVector4d* col )
{ 
	ASSERT( col ); 

	MEMCPY( this, col, sizeof( CVector4d ) * 4 );

	m_nOfArithmeticOpsSinceLastFixup = 0;

	return *this;
}


inline
fixed& CMatrix4d::operator[]( int idx )
{
	ASSERT( idx < 16 );

	return *( ( (fixed*)this ) + idx );
}


inline
fixed const& CMatrix4d::operator[]( int idx ) const
{
	ASSERT( idx < 16 );

	return *( ( (fixed*)this ) + idx );
}

#endif // __CMATRIX4D_H__
